package com.tianji.common.utils;

import cn.hutool.core.bean.copier.CopyOptions;
import com.tianji.coupon.pojo.Coupon;
import org.springframework.beans.FatalBeanException;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.util.ClassUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;

/**
 * Bean操作工具类
 */
public class BeanUtils {

    /**
     * Bean复制，换用Spring的copyProperties方法,防止目标bean使用@Accessors(chain=true)后,CGLib的对象拷贝有问题
     *
     * @param srcBean    源bean对象
     * @param targetBean 目标bean对象
     */
    public static void copyBean(Object srcBean, Object targetBean) {
        if (srcBean == null || targetBean == null) {
            return;
        }
        org.springframework.beans.BeanUtils.copyProperties(srcBean, targetBean);
    }

    /**
     * Bean复制,浅复制
     *
     * @param srcBean     源Bean对象
     * @param targetClass 目标对象Class
     * @param <T>         目标类型
     * @return 目标Bean
     */
    public static <T> T copyBean(Object srcBean, Class<T> targetClass) {
        if (srcBean == null) {
            return null;
        }
        try {
            T targetBean = targetClass.newInstance();
            copyBean(srcBean, targetBean);
            return targetBean;
        } catch (Exception e) {
            throw new RuntimeException("对象拷贝异常", e);
        }
    }

    /**
     * List复制，属浅复制
     *
     * @param srcList     源List对象
     * @param targetClass 目标列表Bean类
     * @param <T>         列表Bean Type
     * @return 目标列表
     */
    public static <T> List<T> copyBeanList(List srcList, Class<T> targetClass) {
        List<T> targetBeanList = new ArrayList<>();
        if (srcList == null || srcList.isEmpty()) {
            return targetBeanList;
        }
        try {
            for (Object srcObj : srcList) {
                T targetBean = targetClass.newInstance();
                copyBean(srcObj, targetBean);
                targetBeanList.add(targetBean);
            }
        } catch (Exception e) {
            throw new RuntimeException("对象拷贝异常", e);
        }
        return targetBeanList;
    }

    /**
     * Bean复制，深度复制
     *
     * @param srcBean     源Bean对象
     * @param targetClass 目标对象Class
     * @param <T>         目标类型
     * @return 目标Bean
     */
    public static <T> T deepCopyBean(Object srcBean, Class<T> targetClass) {
        if (srcBean == null) {
            return null;
        }
        try {
            T targetBean = targetClass.newInstance();
            deepCopyBean(srcBean, targetBean);
            return targetBean;
        } catch (Exception e) {
            throw new RuntimeException("对象拷贝异常", e);
        }
    }

    /**
     * Bean复制，深度复制
     *
     * @param source 源Bean对象
     * @param target 目标Bean对象
     */
    public static void deepCopyBean(Object source, Object target) {
        Class<?> actualEditable = target.getClass();
        PropertyDescriptor[] targetPds = org.springframework.beans.BeanUtils.getPropertyDescriptors(actualEditable);

        for (PropertyDescriptor targetPd : targetPds) {
            Method writeMethod = targetPd.getWriteMethod();
            if (writeMethod != null) {
                PropertyDescriptor sourcePd = org.springframework.beans.BeanUtils.getPropertyDescriptor(source.getClass(), targetPd.getName());
                if (sourcePd != null) {
                    Method readMethod = sourcePd.getReadMethod();
                    if (readMethod != null &&
                            ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
                        try {
                            if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                                readMethod.setAccessible(true);
                            }
                            Object value = readMethod.invoke(source);
                            if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                                writeMethod.setAccessible(true);
                            }
                            //主要的核心在这里：针对Field的类型为Collection类型的处理
                            if (value instanceof Collection) {
                                Type writeType = writeMethod.getGenericParameterTypes()[0];
                                Type readType = readMethod.getGenericReturnType();
                                Type writeActualType;
                                //当 source 中 Collection类型的Field的泛型和target中的Field的泛型类型不一致的时候 需要进行深度拷贝
                                if (writeType instanceof ParameterizedType
                                        && readType instanceof ParameterizedType
                                        && (writeActualType = ((ParameterizedType) writeType).getActualTypeArguments()[0]) != ((ParameterizedType) readType).getActualTypeArguments()[0]) {
                                    //这里只是简单的调用反射来实例化，因此原集合类型必须要有无参构造，比如Arrays.asList创建的集合则无法拷贝，会抛出异常
                                    Collection sourceList = (Collection) value;
                                    Collection actualValue;
                                    //兼容Arrays.asList创建的集合，获取类名为java.util.Arrays$ArrayList，则直接设置actualValue为new ArrayList,避免因没有无参构造调用newInstance报错的问题
                                    if (value.getClass().toString().equals("class java.util.Arrays$ArrayList") || value.getClass().toString().equals("class java.util.Collections$EmptyList")) {
                                        actualValue = new ArrayList();
                                    } else {
                                        actualValue = (Collection) value.getClass().newInstance();
                                    }
                                    actualValue.clear();
                                    for (Object subSource : sourceList) {
                                        Object subTarget = ((Class) writeActualType).newInstance();
                                        deepCopyBean(subSource, subTarget);
                                        actualValue.add(subTarget);
                                    }
                                    value = actualValue;
                                }
                            }
                            writeMethod.invoke(target, value);
                        } catch (Throwable ex) {
                            throw new FatalBeanException(
                                    "Could not copy property '" + targetPd.getName() + "' from source to target", ex);
                        }
                    }
                }
            }
        }
    }

    /**
     * List复制，深度复制
     *
     * @param srcList     源List
     * @param targetClass 目标List元素类
     * @param <T>         目标元素类型
     * @return 目标List
     */
    public static <T> List<T> deepCopyBeanList(List srcList, Class<T> targetClass) {
        return deepCopyBeanList(srcList, targetClass, null);
    }

    /**
     * 深度克隆
     *
     * @param srcList       源List
     * @param targetClass   目标Class
     * @param cloneConsumer 克隆消费类,可处理自定义转换属性
     * @param <T>           目标类型
     * @param <R>           源类型
     */
    public static <T, R> List<T> deepCopyBeanList(List<R> srcList, Class<T> targetClass, BiConsumer<R, T> cloneConsumer) {
        List<T> targetBeanList = new ArrayList<>();
        if (srcList == null || srcList.isEmpty()) {
            return targetBeanList;
        }
        try {
            for (R srcObj : srcList) {
                T targetBean = targetClass.newInstance();
                deepCopyBean(srcObj, targetBean);
                if (cloneConsumer != null) {
                    cloneConsumer.accept(srcObj, targetBean);
                }
                targetBeanList.add(targetBean);
            }
        } catch (Exception e) {
            throw new RuntimeException("对象拷贝异常", e);
        }
        return targetBeanList;
    }


}